#pragma once

#include <queue>
#include <vector>
#include <pthread.h>
#include <cstring>
#include <assert.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "Channel.hpp"
#include "Log.hpp"

using namespace ns_log;

#define CHANNEL_MAP_SIZE 128
#define TIME_OUT 2

class EventLoop;

class Dispatcher
{
public:
    // 初始化 select poll epoll 三者之一需要的数据块
    Dispatcher(EventLoop *evLoop) : _evLoop(evLoop) {}
    virtual ~Dispatcher() {}
    // 添加节点
    virtual int add() {}
    // 删除节点
    virtual int remove() {}
    // 修改节点
    virtual int modify() {}
    // 事件检测
    virtual int dispatch(int timeout = TIME_OUT /* 单位：s*/) {}
    // 改变channel
    void setChannel(Channel *channel)
    {
        _channel = channel;
    }

protected:
    Channel *_channel;
    EventLoop *_evLoop;
};

class EpollDispachter : public Dispatcher
{
public:
    // 初始化 select poll epoll 三者之一需要的数据块
    EpollDispachter(EventLoop *evLoop);
    ~EpollDispachter();
    // 添加节点
    int add();
    // 删除节点
    int remove();
    // 修改节点
    int modify();
    // 事件检测
    int dispatch(int timeout /* 单位：s*/);

private:
    int epollCtl(int op);

private:
    int _epfd;
    epoll_event *_events;
};

// 处理节点的方式
enum ElemType
{
    ADD,
    DELETE,
    MODIFY
};

// 任务队列节点
struct ChannelElement
{
    int type = 0; // 如何处理channel节点
    Channel *channel;

    ChannelElement(int _type, Channel *_channel)
        : type(_type),
          channel(_channel)
    {
    }
};

class EventLoop
{
public:
    EventLoop(const char *threadName = nullptr)
        : _channelMap(CHANNEL_MAP_SIZE, nullptr)
    {
        if (threadName)
            strcpy(_threadName, threadName);
        else
            strcpy(_threadName, "MainThread");
        _dispatcher = new EpollDispachter(this); // 使用epoll模型

        _threadID = pthread_self();

        pthread_mutex_init(&_mutex, nullptr);
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, _socketPair))
        {
            perror("socketpair");
            exit(-1);
        }
        // LOG(INFO) << "socketpair: " << _socketPair[0] << " " << _socketPair[1] << std::endl;
        //  通过给 1 写数据 0就会被激活  这样子线程就会解除阻塞
        Channel *channel = new Channel(_socketPair[0], ReadEvent, readLoackMsg, nullptr, this);
        // 将channel添加到任务队列中
        addTask(channel, ADD);
    }
    // 启动反应堆模型
    int run()
    {
        _isQuit = false;
        // 判断线程是否正常
        if (_threadID != pthread_self())
            return -1;
        // 循环运行进行事件处理
        while (!_isQuit)
        {
            // 检测
            _dispatcher->dispatch();
            // 处理任务队列中的任务
            processTask();
        }
        return 0;
    }
    // 处理激活fd
    int activate(int fd, int event)
    {
        if (fd < 0)
            return -1;
        // 取出channel
        Channel *channel = _channelMap[fd];
        assert(channel->_fd == fd);

        if (event & ReadEvent && channel->_readCallBack)
        {
            channel->_readCallBack(channel->_arg);
        }
        if (event & WriteEvent && channel->_writeCallBack)
        {
            channel->_writeCallBack(channel->_arg);
        }
        return 0;
    }
    // 添加任务
    int addTask(Channel *channel, int type)
    {
        // LOG(INFO) << _threadName << ": add task fd: " << channel->_fd << " " << type << "\n";
        //   加锁
        pthread_mutex_lock(&_mutex);
        ChannelElement *elem = new ChannelElement(type, channel);
        _taskQueue.push(elem);
        pthread_mutex_unlock(&_mutex);

        // 处理任务
        /**
         *  1. 可能是当前线程也可能是主线程进行添加
         *  2. 如果是主线程，则不进行任务的处理，子线程需要进行
         * */
        if (_threadID == pthread_self())
        {
            // 子线程(对子线程而言 不是mainLoop)
            // LOG(INFO) << pthread_self() << ": processTask\n";
            processTask();
        }
        else
        {
            // 主线程  通知子线程进行任务处理
            // 1. 子线程在工作
            // 2. 子线程阻塞了 select poll epoll
            notice();
        }
        return 0;
    }

    int GetThreadID()
    {
        return _threadID;
    }

private:
    int processTask()
    {
        // 加锁
        pthread_mutex_lock(&_mutex);
        while (!_taskQueue.empty())
        {
            ChannelElement *elem = _taskQueue.front();
            // LOG(INFO) << "fd: " << elem->channel->_fd << "\n";
            switch (elem->type)
            {
            case ADD:
                addElem(elem);
                break;
            case DELETE:
                removeElem(elem);
                break;
            case MODIFY:
                modifyElem(elem);
                break;
            }
            _taskQueue.pop();
            delete elem;
        }
        pthread_mutex_unlock(&_mutex);
        // LOG(INFO) << "empty\n";
    }
    void addElem(ChannelElement *elem)
    {
        int fd = elem->channel->_fd;
        // LOG(INFO) << _threadName << ": addElem fd " << fd << "\n";
        //  要存储的fd比map大，就需要进行扩容
        if (fd > _channelMap.size())
        {
            _channelMap.resize(fd * 2);
        }
        // 存储对应关系
        _channelMap[fd] = elem->channel;
        _dispatcher->setChannel(elem->channel);
        _dispatcher->add();
    }
    void removeElem(ChannelElement *elem)
    {
        int fd = elem->channel->_fd;
        if (fd > _channelMap.size() || _channelMap[fd] == nullptr)
            return;
        // 去掉对应关系
        _channelMap[fd] = nullptr;
        _dispatcher->setChannel(elem->channel);
        _dispatcher->remove();
        close(fd);
    }
    void modifyElem(ChannelElement *elem)
    {
        int fd = elem->channel->_fd;
        if (fd > _channelMap.size())
        {
            LOG(ERROR) << "modify error\n";
            return;
        }
        if (_channelMap[fd])
        {
            _dispatcher->setChannel(elem->channel);
            _dispatcher->modify();
        }
    }
    static int readLoackMsg(void *arg)
    {
        // LOG(INFO) << "readLoackMsg\n";
        EventLoop *evLoop = (EventLoop *)arg;
        char buf[128];
        read(evLoop->_socketPair[0], buf, sizeof(buf));
        return 0;
    }
    // 唤醒子线程
    void notice()
    {
        // 只需要写一些数据即可
        // LOG(INFO) << "notice\n";
        write(_socketPair[1], "123", 3);
    }

public:
    bool _isQuit = true;
    Dispatcher *_dispatcher;

    std::queue<ChannelElement *> _taskQueue; // 任务队列
    std::vector<Channel *> _channelMap;      // fd->channel

    pthread_t _threadID;    // 线程ID
    char _threadName[32];   // 线程名
    pthread_mutex_t _mutex; // 互斥锁
    int _socketPair[2];     // 存储本地通信fd _socketPair[0] 发 _socketPair[1] 写
};